package se.rende.myrss.data;

import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.util.Log;
import android.util.Xml;

public class ChannelParser {
	private static final String RSS_NS = "http://purl.org/rss/1.0/";
	private static final String RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
	private static final String DC_NS = "http://purl.org/dc/elements/1.1/";
	
	private ChannelParseListener channelParseListener = null;
	private String channelTitle;
	
	public void setChannelParseListener(ChannelParseListener channelParseListener) {
		this.channelParseListener = channelParseListener;
	}
	
	public static class ChannelItem {
        public String id;
        public String source;
        public String title;
		public String description;
		public String link;
		public String quid;
		public long pubDate;
		public String creator;
		public String sources;

        public ChannelItem(String id, String sources, String source, String title, String description, String link, long pubDate, String creator) {
            this.id = id;
			this.sources = sources;
			this.source = source;
            this.title = title;
			this.description = description;
			this.link = link;
			this.pubDate = pubDate;
			this.creator = creator;
        }

        @Override
        public String toString() {
            return title;
        }
    }

    public void parse(String url) throws URISyntaxException, IOException {
		XmlPullParser parser = Xml.newPullParser();
		DefaultHttpClient defaultHttpClient = new DefaultHttpClient();

		HttpGet method = new HttpGet(url);
		HttpResponse response = defaultHttpClient.execute(method);
		StatusLine status = response.getStatusLine();
		Log.d("ChannelParser", "status.getStatusCode()=" + status.getStatusCode());
		if (status.getStatusCode() == 304) {
			// OK, no new items
		} else if (status.getStatusCode() == 200) {
			InputStream is = response.getEntity().getContent();
			try {
				parser.setInput(is, null);
				parser.nextTag();
				parser.require(XmlPullParser.START_TAG, null, "rss");
				parser.nextTag();
				
				parser.require(XmlPullParser.START_TAG, null, "channel");
				
				// parse all item elements
				while (true) {
					skipToNextTag(parser);
					if (parser.getEventType() == XmlPullParser.START_TAG
							&& "item".equals(parser.getName())) {
						parseItemElement(parser);
					} else if (parser.getEventType() == XmlPullParser.START_TAG
							&& "title".equals(parser.getName())) {
						channelTitle = getElementText(parser);
					} else if (parser.getEventType() == XmlPullParser.END_TAG
							&& "channel".equals(parser.getName())) {
						break;
					} else if (parser.getEventType() == XmlPullParser.END_TAG
							&& "rss".equals(parser.getName())) {
						break;
					}
				}
			} catch (XmlPullParserException e) {
				String msg = "rss xml syntax error on line " + e.getLineNumber() + " col " + e.getColumnNumber();
				throw new IOException(msg, e);
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ParseException e) {
				e.printStackTrace();
			} finally {
				try {
					if (is != null) {
						is.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} else {
			throw new IOException("Invalid response from server: "
					+ status.toString());
		}
	}

	private void parseItemElement(XmlPullParser parser) throws XmlPullParserException, IOException, ParseException {
    	String id = parser.getAttributeValue(RDF_NS, "about");
    	String title = null;
    	String description = null;
    	String link = null;
		Date date = null;
		String creator = null;
		String source = null;
		while (true) {
			parser.next();
			if (parser.getEventType() == XmlPullParser.START_TAG
					&& "title".equals(parser.getName())) {
				title = getElementText(parser);
			} else if (parser.getEventType() == XmlPullParser.START_TAG
					&& "description".equals(parser.getName())) {
				description = getElementText(parser);
			} else if (parser.getEventType() == XmlPullParser.START_TAG
					&& "link".equals(parser.getName())) {
				link = getElementText(parser);
			} else if (parser.getEventType() == XmlPullParser.START_TAG
					&& "pubDate".equals(parser.getName())) {
				String dateText = getElementText(parser);
				date = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US).parse(dateText);
			} else if (parser.getEventType() == XmlPullParser.START_TAG
					&& DC_NS.equals(parser.getNamespace())
					&& "creator".equals(parser.getName())) {
				creator = getElementText(parser);
			} else if (parser.getEventType() == XmlPullParser.START_TAG
					&& "source".equals(parser.getName())) {
				source = getElementText(parser);
			} else if (parser.getEventType() == XmlPullParser.END_TAG
					&& "item".equals(parser.getName())) {
				break;
			}
		}
		if (title != null) {
			addItem(new ChannelItem(id, channelTitle, source, title, description, link, date.getTime(), creator));
		}
	}

	/**
	 * Skip one token and repeat if not start or end tag.
	 * 
	 * @param parser
	 * @throws XmlPullParserException
	 * @throws IOException
	 */
	private void skipToNextTag(XmlPullParser parser)
			throws XmlPullParserException, IOException {
		while (true) {
			parser.next();
			if (parser.getEventType() == XmlPullParser.START_TAG 
					|| parser.getEventType() == XmlPullParser.END_TAG 
					|| parser.getEventType() == XmlPullParser.END_DOCUMENT) {
				break;
			}
		}
	}

    private void skipElement(XmlPullParser parser) throws XmlPullParserException, IOException {
		String tagNamespace = parser.getNamespace();
		String tagName = parser.getName();
		int level = 1;
		while (true) {
			parser.next();
			if (parser.getEventType() == XmlPullParser.START_TAG) {
				level++;
			} else if (parser.getEventType() == XmlPullParser.END_TAG) {
				level--;
				if (level == 0 
						&& tagNamespace.equals(parser.getNamespace()) 
						&& tagName.equals(parser.getName())) {
					break;
				}
			}
		}
	}

    public Date parseISO8691Date(final String iso8601string)
            throws ParseException {
        String s = iso8601string.replace("Z", "+00:00");
        try {
            s = s.substring(0, 22) + s.substring(23);
        } catch (IndexOutOfBoundsException e) {
            throw new ParseException("Invalid length", 0);
        }
        Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(s);
        return date;
    } 
    
    private String getElementText(XmlPullParser parser) throws XmlPullParserException, IOException {
		String tagNamespace = parser.getNamespace();
		String tagName = parser.getName();
		String text = parser.nextText().trim();
		parser.require(XmlPullParser.END_TAG, tagNamespace, tagName);
		return text;
	}

	private void addItem(ChannelItem item) {
        if (channelParseListener != null) {
        	channelParseListener.addItem(item);
        }
    }

    public interface ChannelParseListener {
    	void addItem(ChannelItem item);
	}

}
